home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacHack 1997
/
MacHack 1997.toast
/
Hacks
/
Hacks ’97
/
Bugs in My Serial
/
Bugs in My Serial code
/
MacsBugSerialStuff.c
next >
Wrap
C/C++ Source or Header
|
1997-06-28
|
10KB
|
464 lines
#include <lowmem.h>
#include "MacsBugSerialStuff.h"
#include <timer.h>
static OSErr SerialDriverConfigureSCC(void);
static OSErr SerialDriverCheckForIOP(void);
static OSErr SerialDriverPortAvailable(void);
static pascal UInt32 SerialDriverGetMsecs(void);
enum
{
kPortNotAvailable = 1024,
kTimedOut = 1025,
kNotImplemented = 1026
};
static void SynchronizeIO(void) = 0x4E71; // NOP
// PORT_AVAIL_FLAG is the value in the PortAUse or PortBUse low mem
// which marks a serial port as free; NUB_PORT_IN_USE_FLAG is the value
// we write into that low mem when we take over a port (but if EtherTalk
// is on, we have to handle port B a little differently; see below)
#define PORT_AVAIL_FLAG 0xFF
#define NUB_PORT_IN_USE_FLAG 3
// The port we are using (printer or modem)
typedef enum
{
kModemPort = 0,
kPrinterPort = 1
} SCCIOPort;
SCCIOPort gSerialDriverPort;
// The baud rate we are using (19200 or 57600).
UInt16 gSerialDriverBaudRate;
// Whether we should time out the host connection, or hang forever.
Boolean gSerialDriverAllowCommTimeouts;
// Whether the nub needs to concern itself with turning on the SCC.
Boolean gSerialDriverSCCIsPowerManaged;
// Pointers to SCC read and write channels.
volatile UInt8* gSCCWrCtlPort;
volatile UInt8* gSCCWrDataPort;
volatile UInt8* gSCCRdCtlPort;
volatile UInt8* gSCCRdDataPort;
static OSErr SerialDriverConfigureSCC(void)
{
short i;
// SCC configuration table
#define SCC_CONFIG_TBL_SIZE 34
UInt8 SCCConfigTable[] =
{
9, 0x80, // WR 9, reset channel, SCC interrupts disabled
4, 0x44, // WR 4, 16x clock, 1 stop bit, no parity
3, 0xC0, // WR 3, Rx 8 bits, disabled
5, 0xE2, // WR 5, Tx 8 bits, DTR and RTS asserted, transmitter disabled
2, 0x00, // WR 2, zero interrupt vector
10, 0x00, // WR 10, NRZ encoding
11, 0x50, // WR 11, baud rate generator clock to receiver, transmitter
12, 0x00, // WR 12, low byte baud rate (57.6K) -- 3.6864 MHz, 16x clock
13, 0x00, // WR 13, high byte baud rate (9.6K up)
3, 0xC1, // WR 3, Rx 8 bits, enabled
5, 0xEA, // WR 5, Tx 8 bits, DTR and RTS asserted, transmitter enabled
14, 0x01, // WR 14, enable baud rate generator
15, 0x80, // WR 15, enable break/abort interrupts
0, 0x10, // WR 0, reset ext/status interrupts
0, 0x10, // WR 0, reset ext/status interrupts (again)
1, 0x01, // WR 1, enable ext/status interrupts
9, 0x0A // WR 9, SCC interrupts enabled (MIE), status in low bits
};
// assumes that the SCC is accessible (see SerialDriverCheckForIOP below),
// and that we're called with interrupts off
// make sure we reset the right channel
SCCConfigTable[1] = (gSerialDriverPort == kModemPort) ? 0x80 : 0x40;
// set the baud rate to 19200 or 57600
SCCConfigTable[15] = (gSerialDriverBaudRate == 19200) ? 4 : 0;
// write configuration table info to the SCC
for (i = 0; i < SCC_CONFIG_TBL_SIZE; i += 2)
{
*gSCCWrCtlPort = SCCConfigTable[i]; // select the register
SynchronizeIO();
*gSCCWrCtlPort = SCCConfigTable[i + 1]; // write it
SynchronizeIO();
}
return noErr;
}
static OSErr SerialDriverCheckForIOP(void)
{
UInt8 sccRR1;
// If we're on a machine with an IOP chip, it must be set to bypass
// mode for direct SCC access, or we'll get bus errors. To verify that
// we can really touch the SCC, we try to read RR1, which involves
// both a read and a write to the chip.
*gSCCWrCtlPort = 1; // select RR1
SynchronizeIO();
sccRR1 = *gSCCRdCtlPort; // read RR1
SynchronizeIO();
return noErr;
}
#pragma parameter __D0 RequestPortBFromLAPManager
OSErr RequestPortBFromLAPManager() = {0x2278, 0x0B18, 0x7011, 0x7203, 0x4EA9, 0x0002};
static char LMGetPortAUse()
{
return *((Ptr)0x290);
}
static OSErr SerialDriverPortAvailable(void)
{
// 0x2278, 0x0B18, // MOVEA.L LAPMgrPtr,A1
// 0x7011, // MOVEQ #LUsePortB,D0
// 0x7203, // MOVEQ #3,D1
// 0x4EA9, 0x0002, // JSR LAPMgrCall(A1)
// Mark our chosen port as in use.
if ((gSerialDriverPort == kModemPort) && (LMGetPortAUse() == PORT_AVAIL_FLAG))
{
// LMSetPortAUse(NUB_PORT_IN_USE_FLAG);
return noErr;
}
if (gSerialDriverPort == kPrinterPort)
{
if (LMGetPortBUse() == PORT_AVAIL_FLAG)
{
// LMSetPortBUse(NUB_PORT_IN_USE_FLAG);
return noErr;
}
else
{
return RequestPortBFromLAPManager();
}
}
return kPortNotAvailable;
}
pascal OSErr SerialDriver_Open(void)
{
short ctlPortOffset;
short dataPortOffset;
OSErr result;
long gestaltResponse;
// ••• these should be configurable
gSerialDriverPort = kPrinterPort; // ••• kModemPort for single-port PowerBooks
gSerialDriverBaudRate = 57600;
gSerialDriverAllowCommTimeouts = false; // for now, this should never be true (no timer)
gSerialDriverSCCIsPowerManaged = false; // assume we're not on a power-tasic machine
// Make sure we're not colliding with LocalTalk or somebody else.
if (SerialDriverPortAvailable() != noErr)
{
return kPortNotAvailable;
}
// Set up pointers to SCC read and write channels.
ctlPortOffset = (gSerialDriverPort == kModemPort) ? 2 /* aCtl */ : 0 /* bCtl */;
dataPortOffset = (gSerialDriverPort == kModemPort) ? 6 /* aData */ : 4 /* bData */;
gSCCWrCtlPort = (UInt8*)LMGetSCCWr() + ctlPortOffset;
gSCCWrDataPort = (UInt8*)LMGetSCCWr() + dataPortOffset;
gSCCRdCtlPort = (UInt8*)LMGetSCCRd() + ctlPortOffset;
gSCCRdDataPort = (UInt8*)LMGetSCCRd() + dataPortOffset;
// Make sure we can access the SCC directly.
// if ((result = SerialDriverCheckForIOP()) != noErr)
// {
// return result;
// }
// If we are on a machine with a Power Managed SCC then we need to record that so
// we can make sure it is powered up each time we are entered.
result = Gestalt(gestaltPowerMgrAttr, &gestaltResponse);
if (result == noErr)
{
// First make sure the Power Mgr API is available on this box.
if (gestaltResponse & (1 << gestaltPMgrDispatchExists))
{
// Now find out if the SCC is power managed and save an indication for later.
if (gestaltResponse & (1 << gestaltPMgrSCC))
{
gSerialDriverSCCIsPowerManaged = true;
}
}
}
result = SerialDriver_EnableSCCPower();
// This is necessary for machines with non-power managed SCCs. SerialDriver_EnableSCCPower
// does nothing on those machines.
if (result == noErr)
{
result = SerialDriver_Reset();
SerialDriver_SendString("Welcome to MacsBug\n\n");
}
return result;
}
pascal OSErr SerialDriver_Reset()
{
// This assumes SerialDriver_StartIO has already been called, which is true since
// each time it is called, we are called.
return SerialDriverConfigureSCC();
}
pascal OSErr SerialDriver_EnableSCCPower()
{
return noErr;
/*
// This assumes SerialDriver_StartIO has already been called, which is true since
// each time it is called, it calls us.
if (gSerialDriverSCCIsPowerManaged)
{
if (gSerialDriverPort == kModemPort)
{
AOnIgnoreModem();
}
else
{
BOn();
}
// Since we've just (possibly) turned on the power to the SCC, we need to
// reinitialize it.
return SerialDriver_Reset();
}
// If the machine does not have power managed serial ports, we do nothing.
return noErr;
*/
}
pascal Boolean SerialDriver_BytePresent()
{
UInt8 sccRR0 = *gSCCRdCtlPort;
SynchronizeIO();
return sccRR0 & 0x01;
}
static pascal UInt32 SerialDriverGetMsecs()
{
//••• return (SerialDriverGetTime() / 10); // returns tenths of a msec
return 0;
}
pascal Boolean SerialDriver_WaitForByte(UInt16 timeoutMillisecs)
{
UInt8 sccRR0;
UInt32 currentTime, cutoffTime;
if (gSerialDriverAllowCommTimeouts)
{
currentTime = SerialDriverGetMsecs();
cutoffTime = currentTime + timeoutMillisecs;
}
while (1) // wait until there's a byte there (or we time out)
{
sccRR0 = *gSCCRdCtlPort;
SynchronizeIO();
if (sccRR0 & 0x01)
{
// We got a byte.
return true;
}
if (gSerialDriverAllowCommTimeouts)
{
// Still waiting - see if we've timed out.
if (SerialDriverGetMsecs() > cutoffTime)
{
return false; // we timed out waiting for a byte
}
}
}
}
pascal OSErr SerialDriver_ReceiveByte(UInt8 *inByte, UInt16 timeoutMillisecs)
{
UInt8 recvdByte;
if (!SerialDriver_WaitForByte(timeoutMillisecs))
{
return kTimedOut;
}
recvdByte = *gSCCRdDataPort;
SynchronizeIO();
*inByte = recvdByte;
return noErr;
}
void SerialDriver_SendString(const char *, ...)
{
// Ideally, we'd allow printf-style format strings through this function. However, calling
// any of the XXprintf functions brings in statically initialized data, which we can't have.
// I really should fix MacsBug to allow it.
// va_list args;
// char localString[512];
// va_start(args, format);
// vsprintf(localString, format, args);
// va_end(args);
// SerialDriver_SendBytes(format, strlen(format));
}
pascal void SerialDriver_SendBytes(UInt8 *outBytes, UInt16 count)
{
UInt8 sccRR0, theByte;
while (count--)
{
// Make sure the transmitter is idle.
do
{
sccRR0 = *gSCCRdCtlPort;
SynchronizeIO();
}
while ((sccRR0 & 0x04) == 0);
theByte = *outBytes++;
*gSCCWrDataPort = theByte;
SynchronizeIO();
}
}
pascal OSErr SerialDriver_FlushIncomingBuffers()
{
UInt8 sccRR0, recvdByte;
// Flush the on-chip receive buffer.
do
{
sccRR0 = *gSCCRdCtlPort;
SynchronizeIO();
if (sccRR0 & 0x01)
{
recvdByte = *gSCCRdDataPort;
SynchronizeIO();
}
}
while (sccRR0 & 0x01);
return noErr;
}
pascal OSErr SerialDriver_SendBreak()
{
return kNotImplemented;
}
pascal Boolean SerialDriver_BreakPending()
{
UInt8 sccRR0;
sccRR0 = *gSCCRdCtlPort;
SynchronizeIO();
return ((sccRR0 & 0x80) != 0);
}